#!/usr/bin/env python3
"""
STM32 AESSide-Channel AttackTrace可视化Tool
FromNPZFileReadTraceData并绘制Analyze图表
"""

import argparse
import numpy as np
import matplotlib.pyplot as plt
import os
from datetime import datetime

def plot_traces(npz_file: str, output_dir: str = None):
    """绘制NPZTraceData"""
    print(f"正InReadData: {npz_file}")
    
    # LoadData
    data = np.load(npz_file, allow_pickle=True)
    
    # PrintData结构
    print("\n=== Data结构 ===")
    for key in data.keys():
        if hasattr(data[key], 'shape'):
            print(f"  {key}: shape={data[key].shape}, dtype={data[key].dtype}")
        else:
            print(f"  {key}: {data[key]}")
    
    # Check必要字段
    if 'cpu_traces' not in data:
        print("Error: 缺少cpu_traces字段")
        return False
    
    cpu_traces = data['cpu_traces']
    num_traces = data.get('num_traces', cpu_traces.shape[0])
    durations = data.get('durations', np.zeros(num_traces))
    
    # ConvertoperationForString
    operation = data.get('operation', 'unknown')
    if isinstance(operation, np.ndarray):
        operation = str(operation.item()) if operation.size == 1 else str(operation)
    
    key = data.get('key', None)
    
    print(f"\n=== Data摘要 ===")
    print(f"  Trace数量: {num_traces}")
    print(f"  Trace长度: {cpu_traces.shape[1]} 样本点")
    print(f"  操作Class型: {operation}")
    if key is not None and len(key) > 0:
        print(f"  Key: {bytes(key).hex()}")
    print(f"  平均持续时间: {np.mean(durations):.6f}秒")
    
    # Settings绘图样式
    plt.style.use('default')
    plt.rcParams['font.size'] = 10
    plt.rcParams['axes.titlesize'] = 12
    plt.rcParams['axes.labelsize'] = 10
    
    # Create图表布局 (3行)
    fig = plt.figure(figsize=(16, 12))
    
    # 1. PowerTrace对比图 (顶部)
    ax1 = plt.subplot2grid((3, 2), (0, 0), colspan=2)
    
    # Display多条Trace（最多20条）
    display_count = num_traces # min(20, num_traces)
    colors = plt.cm.viridis(np.linspace(0, 1, display_count))
    for i in range(display_count):
        # Filter非零Partial
        trace = cpu_traces[i]
        non_zero_idx = np.where(trace != 0)[0]
        if len(non_zero_idx) > 0:
            ax1.plot(non_zero_idx, trace[non_zero_idx], 
                    color=colors[i], alpha=0.6, linewidth=0.8)
    
    # 绘制平均Trace
    mean_trace = np.mean(cpu_traces, axis=0)
    non_zero_mean = mean_trace[mean_trace != 0]
    if len(non_zero_mean) > 0:
        ax1.plot(mean_trace, color='red', linewidth=2, label='Average Trace')
    
    ax1.set_title(f'Power Consumption Traces - {operation.upper()}', fontweight='bold')
    ax1.set_xlabel('Sample Point')
    ax1.set_ylabel('CPU Usage (%)')
    ax1.legend()
    ax1.grid(True, alpha=0.3)
    
    # 2. Power分布直方图 (第二行左)
    ax2 = plt.subplot2grid((3, 2), (1, 0))
    
    # CPUUse率分布
    cpu_flat = cpu_traces[cpu_traces > 0].flatten()
    ax2.hist(cpu_flat, bins=50, color='steelblue', alpha=0.7, edgecolor='black')
    ax2.set_title('CPU Usage Distribution')
    ax2.set_xlabel('CPU Usage (%)')
    ax2.set_ylabel('Frequency')
    ax2.grid(True, alpha=0.3, axis='y')
    
    # Add统计线
    mean_cpu = np.mean(cpu_flat)
    ax2.axvline(mean_cpu, color='red', linestyle='--', linewidth=2, label=f'Mean: {mean_cpu:.2f}%')
    ax2.legend()
    
    # 3. Trace持续时间 (第二行右)
    ax3 = plt.subplot2grid((3, 2), (1, 1))
    
    ax3.plot(durations, marker='o', markersize=3, linestyle='-', linewidth=0.5, alpha=0.7)
    ax3.set_title('Trace Duration')
    ax3.set_xlabel('Trace Index')
    ax3.set_ylabel('Duration (seconds)')
    ax3.grid(True, alpha=0.3)
    
    # Add平均线
    mean_duration = np.mean(durations)
    ax3.axhline(mean_duration, color='red', linestyle='--', linewidth=2, 
               label=f'Mean: {mean_duration:.6f}s')
    ax3.legend()
    
    # 4. 热力图 (第三行左)
    ax4 = plt.subplot2grid((3, 2), (2, 0))
    
    # Display前100条Trace的热力图
    display_heatmap = min(100, num_traces)
    heatmap_data = cpu_traces[:display_heatmap]
    
    # Filter全零列
    non_zero_cols = np.any(heatmap_data != 0, axis=0)
    if np.sum(non_zero_cols) > 0:
        heatmap_data = heatmap_data[:, non_zero_cols]
    
    im = ax4.imshow(heatmap_data, aspect='auto', cmap='hot', interpolation='nearest')
    ax4.set_title(f'Power Traces Heatmap (First {display_heatmap} Traces)')
    ax4.set_xlabel('Sample Point')
    ax4.set_ylabel('Trace Index')
    plt.colorbar(im, ax=ax4, label='CPU Usage (%)')
    
    # 5. 统计摘要 (第三行右)
    ax5 = plt.subplot2grid((3, 2), (2, 1))
    ax5.axis('off')
    
    # Calculate统计Information
    cpu_valid = cpu_traces[cpu_traces > 0]
    if len(cpu_valid) > 0:
        mean_cpu = np.mean(cpu_valid)
        std_cpu = np.std(cpu_valid)
        min_cpu = np.min(cpu_valid)
        max_cpu = np.max(cpu_valid)
    else:
        mean_cpu = np.mean(cpu_traces)
        std_cpu = np.std(cpu_traces)
        min_cpu = np.min(cpu_traces)
        max_cpu = np.max(cpu_traces)
    
    # Calculate每条Trace的Has效样本数
    valid_samples_per_trace = np.sum(cpu_traces > 0, axis=1)
    mean_samples = np.mean(valid_samples_per_trace)
    
    stats_text = f"""STATISTICAL SUMMARY
    
Total Traces: {num_traces}
Max Trace Length: {cpu_traces.shape[1]} samples
Mean Valid Samples: {mean_samples:.1f}

CPU Usage Statistics:
  Mean: {mean_cpu:.2f}%
  Std:  {std_cpu:.2f}%
  Min:  {min_cpu:.2f}%
  Max:  {max_cpu:.2f}%

Timing Statistics:
  Mean Duration: {mean_duration:.6f}s
  Total Time: {np.sum(durations):.3f}s

Operation: {operation.upper()}
File: {os.path.basename(npz_file)}
    """
    
    ax5.text(0.05, 0.95, stats_text, transform=ax5.transAxes, 
            fontsize=9, verticalalignment='top', fontfamily='monospace',
            bbox=dict(boxstyle="round,pad=0.5", facecolor="lightgray", alpha=0.8))
    
    plt.tight_layout()
    
    # Save图像
    if output_dir is None:
        output_dir = os.path.dirname(npz_file) or '.'
    
    base_name = os.path.basename(npz_file).replace('.npz', '')
    plot_filename = os.path.join(output_dir, f'{base_name}_analysis.png')
    
    plt.savefig(plot_filename, dpi=300, bbox_inches='tight')
    print(f"\n[OK] 图像已Save: {plot_filename}")
    
    # Display图像
    plt.show()
    
    # PrintDetailed统计
    print("\n=== Detailed统计 ===")
    print(f"CPUUse率范围: {min_cpu:.2f}% - {max_cpu:.2f}%")
    print(f"平均CPUUse率: {mean_cpu:.2f}% (±{std_cpu:.2f}%)")
    print(f"平均Has效样本数: {mean_samples:.1f}")
    print(f"平均Trace时长: {mean_duration:.6f}秒")
    
    return True

def main():
    parser = argparse.ArgumentParser(
        description='STM32 AESSide-Channel AttackTrace可视化Tool',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Example:
  # 绘制TraceData
  python3 plot_traces.py traces.npz
  
  # 指定Output directory
  python3 plot_traces.py traces.npz --output ./plots
        """
    )
    
    parser.add_argument('npz_file', type=str, help='NPZTraceFilePath')
    parser.add_argument('--output', '-o', type=str, default=None,
                       help='Output图像Directory (Default: NPZFile所InDirectory)')
    
    args = parser.parse_args()
    
    # CheckFile存In性
    if not os.path.exists(args.npz_file):
        print(f"Error: FileNot存In: {args.npz_file}")
        return 1
    
    # CreateOutput directory
    if args.output and not os.path.exists(args.output):
        os.makedirs(args.output)
        print(f"CreateOutput directory: {args.output}")
    
    # 绘制Data
    success = plot_traces(args.npz_file, args.output)
    
    return 0 if success else 1

if __name__ == '__main__':
    exit(main())
